package com.onpositive.image.fitting.tools;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.RenderingHints.Key;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.util.HashMap;
import java.util.Map;
import java.awt.RenderingHints;

public class TransformerFactory
{
	private RenderingHints rHints ;
	private AffineTransformOp halfShrinkTransform ;
	private AffineTransformOp identityTransform ;
	
	private static TransformerFactory instance = null ;
	
	public static class TransformParam{
		
		private AffineTransformOp tOp ;
		private int dstWidth ;
		private int dstHeight ;
		
		private TransformParam( AffineTransformOp tOp, int dstWidth, int dstHeight )
		{
			this.tOp = tOp ;
			this.dstWidth = dstWidth ;
			this.dstHeight = dstHeight ;
		}
	
		public AffineTransformOp getTransformOp() {
			return tOp;
		}
		
		public int getDstWidth() {
			return dstWidth;
		}

		public int getDstHeight() {
			return dstHeight;
		}
		
	}
	
	public static TransformerFactory getInstance()
	{
		if( instance == null  )
			instance = new TransformerFactory() ;
		
		return instance ;
	}
	
	private TransformerFactory(){
		
		HashMap<RenderingHints.Key, Object> hintMap = new HashMap<RenderingHints.Key, Object>() ;
		hintMap.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ) ;
		hintMap.put( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC ) ;
		
		rHints = new RenderingHints( hintMap ) ;
		
		halfShrinkTransform =  new AffineTransformOp( new AffineTransform( 0.5, 0, 0, 0.5, 0, 0 ), rHints ) ;
		identityTransform =  new AffineTransformOp( new AffineTransform( 1, 0, 0, 1, 0, 0 ), rHints ) ;
	}		
	
	public AffineTransformOp createRoughResizeTransform( Image source, Rectangle destRect, int scale )
	{
		
		double xRatio = ( scale * destRect.getWidth()  +1 ) / source.getWidth (null) ;
		double yRatio = ( scale * destRect.getHeight() +1 ) / source.getHeight(null) ;
		
		double ratio = xRatio > yRatio ? xRatio : yRatio ;
		ratio *= scale ;	
		
		AffineTransform transform = new AffineTransform( ratio, 0, 0, ratio, 0, 0 ) ;
		
		AffineTransformOp result = new AffineTransformOp( transform, rHints ) ;
		
		return result ;
	}
	
	public TransformParam createRoughResizeParam( Image source, Rectangle destRect, int scale )
	{
		double srcWidth = source.getWidth (null);
		double srcHeight = source.getHeight(null);
		double dstBound_X = scale * destRect.getWidth();
		double dstBound_Y = scale * destRect.getHeight();
		
		double xRatio = ( dstBound_X  +1 ) / srcWidth  ;		
		double yRatio = ( dstBound_Y +1 ) / srcHeight ;
		
		boolean cutTo_X = xRatio < yRatio ;
		boolean cutTo_Y = !cutTo_X ;
		double ratio = cutTo_X ? xRatio : yRatio ;
		
		AffineTransform transform = new AffineTransform( ratio, 0, 0, ratio, 0, 0 ) ;
		
		AffineTransformOp tOp = new AffineTransformOp( transform, rHints ) ;
		
		int dstWidth  = cutTo_X ? (int)( dstBound_X + 0.5 ) : (int)( srcWidth * ratio + 0.5) ;
		int dstHeight = cutTo_Y ? (int)( dstBound_Y + 0.5 ) : (int)( srcHeight * ratio + 0.5) ;
				
		return new TransformParam(tOp, dstWidth, dstHeight) ;
	}
	
	public AffineTransformOp getHalfShrinkTransform()
	{
		return halfShrinkTransform ;
	}

	public AffineTransformOp getIdentityTransform() {
		return identityTransform;
	}
}
